//
//  QYTCPService.swift
//  QYSocketDemo2
//
//  Created by blazer on 2022/9/28.
//

import Foundation

public enum QYHTTPSocketError: Error {
    case connectFailed
    case timeout
    case noData
    case eof
}

@objc public class QYHTTPSocket: NSObject {
    //等待多久时间关闭链接 单位:秒 default 0
    @objc public var waitTime: Int {
        set {
            wTimer = newValue
        }
        get {
            return wTimer
        }
    }
    
    //default = 2
   @objc public var readTimeInterval: TimeInterval {
        set {
            rTimeout = newValue
        }
        get {
            return rTimeout
        }
    }
    
    //default = 1
    @objc public var writeTimeInterval: TimeInterval {
        set {
            wTimeout = newValue
        }
        get {
            return wTimeout
        }
    }
    
    //default = 1000
    @objc public var maxLenght: Int {
        set {
            mLength = newValue
        }
        get {
            return mLength
        }
    }
    
    // defalut = true
    @objc public var isReadData: Bool {
        set {
            isRead = newValue
        }
        get {
            return isRead
        }
    }
    
    @objc public required init(_ host: String, port: Int) {
        self.host = host
        self.port = port
        configure.allowsCellularAccess = false
        configure.httpShouldUsePipelining = true
        urlSession = URLSession.init(configuration: configure)
        super.init()
        
    }
    
    @objc public func request(_ msg: Data, response: @escaping (Data?, Error?) -> Void) {
        if isConnect { // 直接写
            writeData(msg, completion: response)
        } else {
            if openTask() { // 连接成功
                writeData(msg, completion: response)
            } else { // 连接失败
                response(nil, QYHTTPSocketError.connectFailed)
            }
        }
    }
    
    @objc public func close() {
        closeTask()
    }
    
    deinit {
        if isConnect || urlStreamTask != nil {
            closeTask()
        }
        if waitTimer != nil {
            waitTimer?.cancel()
            waitTimer?.setEventHandler(handler: nil)
            waitTimer = nil
        }
        self.urlSession.invalidateAndCancel()
    }
    
    private let host: String
    private let port: Int
    private var wTimer: Int = 0
    private var wTimeout: TimeInterval = 1
    private var rTimeout: TimeInterval = 2
    private var mLength: Int = 1000
    private var isConnect: Bool = false
    private var quit: Bool = false
    private var isRead: Bool = true
    
    private var urlSession: URLSession! = nil
    private var urlStreamTask: URLSessionStreamTask? = nil
    private var configure: URLSessionConfiguration = URLSessionConfiguration.default
    
    private var runTime: Int = 0
    private var waitTimer: DispatchSourceTimer? = nil
}

extension QYHTTPSocket {
    private func openTask() -> Bool {
        urlStreamTask = urlSession.streamTask(withHostName: host, port: port)
        guard let task = urlStreamTask else {
            isConnect = false
            return false
        }
        task.resume()
        isConnect = true
        return true
    }

    private func closeTask(){
        if !isConnect {
            urlStreamTask = nil
            isConnect = false
            return
        }
        
        guard let task = urlStreamTask else {
            return
        }
        
        if task.state != .completed {
            task.closeWrite()
            task.closeRead()
        }
        isConnect = false
        urlStreamTask = nil
        
        if waitTimer != nil { // 如果有timer 挂起timer
            waitTimer?.cancel()
            waitTimer?.setEventHandler(handler: nil)
            waitTimer = nil
            
        }
    }
    
    private func writeData(_ msg: Data, completion: @escaping (Data?, Error?) -> Void) {
        if quit == true || isConnect == false || urlStreamTask == nil {
            completion(nil, nil)
            return
        }
        runTime = 0
        judageTimerAction()
        
        urlStreamTask!.write(msg, timeout: self.wTimeout) { [weak self] error in
            guard let strongSelf = self else {
                completion(nil, nil)
                return
            }
            if (error != nil) { //需要重连
                strongSelf.closeTask()
                completion(nil, error)
            } else {
                if strongSelf.isRead {
                    strongSelf.readData { data, error in
                        completion(data, error)
                    }
                } else {
                    completion(nil, nil)
                }
            }
        }
    }
    
    private func readData(response: @escaping (Data?, Error?) -> Void) {
        if quit == true || isConnect == false || urlStreamTask == nil {
            response(nil, nil);
            return
        }
        
        urlStreamTask!.readData(ofMinLength: 1, maxLength: mLength, timeout: rTimeout) { [weak self] data, iscomplete, error in
            guard let strongSelf = self else {
                response(nil, nil)
                return
            }
            if error != nil { // 需要重连
                strongSelf.closeTask()
                response(data, error)
            } else {
                response(data, error)
            }
            if strongSelf.waitTime <= 0 {
                strongSelf.closeTask()
            }
        }
    }
    
    // 处理time 是创建还是激活
    private func judageTimerAction() {
        if waitTime > 0 {
            if waitTimer == nil {
                createTimer()
            }
        }
    }
    
    // 创建timer
    private func createTimer(){
        waitTimer = DispatchSource.makeTimerSource(flags: .strict, queue: DispatchQueue.global())
        guard let timer = waitTimer else{
            return
        }
        timer.schedule(deadline: .now(), repeating: .seconds(1), leeway: .milliseconds(0))
        timer.setEventHandler(handler: { [weak self] in
            guard let strongSelf = self else {
                return
            }

            
            if strongSelf.runTime >= strongSelf.waitTime {
                strongSelf.closeTask()
                return
            }
            strongSelf.runTime += 1
        })
        timer.resume()
    }
}
